#include "methodchecker.h"
#include "../core/nodes/list.h"
#include "../core/nodes/operators.h"
#include "../core/nodes/calls.h"
#include "method.h"
#include "classsemantic.h"
#include "visitordata.h"
#include "error.h"

void MethodChecker::visitClass()
{

}

void MethodChecker::postVisitClass()
{
}
void MethodChecker::visitMethod()
{
	Method * method=this->data()->CurrentMethod;
	if (method->routine()==NULL)
	{
		if ((method->methodType() & FDT_ABSTRACT) ==0)
		{
			method->setRoutine(new Compound(YYLTYPE(),"AbstractNode"));
		}
	}
	if ((method->methodType() & FDT_ABSTRACT) ==0  && method->shouldCompileMainMethod())
	{
		if (this->transformCode())
		{
			if (this->checkLogic())
			{
			  if (method->returnType()!=NULL)
				this->checkResult();
			  //Add a method initialization
			  this->addInitCode();
			  this->commonCheck();
			}
		}
	}
}

bool MethodChecker::transformCode()
{
  Compound * routine=this->data()->CurrentMethod->routine();
  bool ok=true;
  for (unsigned int i=0;i<routine->vector().size();i++)
  {
  	  ok=ok && routine->vector()[i]->transformCode(this->data());
  }
  return ok;
}
bool MethodChecker::checkLogic()
{
  Compound * routine=this->data()->CurrentMethod->routine();
  bool ok=true;
  for (unsigned int i=0;i<routine->vector().size();i++)
  {
	  AbstractNode * node=routine->vector()[i];
	  bool result=node->checkLogic(this->data());
  	  ok=ok && result;
  }
  return ok;
}


void MethodChecker::checkResult()
{
  Compound * routine=this->data()->CurrentMethod->routine();
  bool ok=false;
  for (unsigned int i=0;i<routine->vector().size();i++)
  {
	  AbstractNode * node=routine->vector()[i];
	  bool result=node->isResultReturn();
  	  ok=ok || result;
  }
  if (ok==false)
	  this->data()->Classes->add(new NoResult(routine));
}


static void push_front(Compound * compound, AbstractNode * node)
{
	compound->vector().insert(compound->vector().begin(),node);
}

//Prepends initialization of super and streams
void MethodChecker::addInitCode()
{
	Compound * routine=this->data()->CurrentMethod->routine();
	std::string * input=new std::string("STD_INPUT");
	std::string * output=new std::string("STD_OUTPUT");
	std::string * init1=new std::string("INIT");
	std::string * init2=new std::string("INIT");
	Compound * arg1=new Compound(YYLTYPE(),"AbstractNode");
	Compound * arg2=new Compound(YYLTYPE(),"AbstractNode");
	arg1->own();
	arg2->own();
	CreationCall * call1=new CreationCall(YYLTYPE(),NULL,input,init1,arg1);
	call1->own();
	CreationCall * call2=new CreationCall(YYLTYPE(),NULL,output,init2,arg2);
	call2->own();
	push_front(routine,call2);
	push_front(routine,call1);

	//Add precursor index
	if (this->data()->CurrentMethod->findLocalVar("PRECURSOR")!=-1)
	{
		IdentifierNode * precursor=new IdentifierNode(YYLTYPE(),new std::string("PRECURSOR"));
		precursor->own();
		IdentifierNode * current=new IdentifierNode(YYLTYPE(),new std::string("CURRENT"));
		current->own();
		OperatorNode * assign=new OperatorNode(YYLTYPE(),precursor,current,":=");
		assign->own();
		push_front(routine,assign);
	}
}

void MethodChecker::commonCheck()
{
  Compound * routine=this->data()->CurrentMethod->routine();
  for (unsigned int i=0;i<routine->vector().size();i++)
  {
	  AbstractNode * node=routine->vector()[i];
	  node->checkCommonErrors(this->data());
  }
}

void MethodChecker::visitField()
{
}
